package gofun

import (
	"context"
	"fmt"
	"log"
	"math"
	"math/rand"
	"time"
)

// RetryConfig 重试配置
type RetryConfig struct {
	MaxAttempts  int           // 最大尝试次数（默认3）
	InitialDelay time.Duration // 初始延迟（默认1s）
	MaxDelay     time.Duration // 最大延迟（默认10s）
	JitterFactor float64       // 抖动因子0-1（默认0.2）
}

// ExponentialBackoffRetry 指数退避重试函数
// ctx: 上下文控制
// fn: 要重试的函数
// config: 配置（传nil使用默认配置）
func ExponentialBackoffRetry(ctx context.Context, fn func() error, config *RetryConfig) error {
	// 设置默认配置
	cfg := RetryConfig{
		MaxAttempts:  3,
		InitialDelay: 1 * time.Second,
		MaxDelay:     10 * time.Second,
		JitterFactor: 0.2,
	}

	// 应用自定义配置
	if config != nil {
		if config.MaxAttempts > 0 {
			cfg.MaxAttempts = config.MaxAttempts
		}
		if config.InitialDelay > 0 {
			cfg.InitialDelay = config.InitialDelay
		}
		if config.MaxDelay > 0 {
			cfg.MaxDelay = config.MaxDelay
		}
		if config.JitterFactor >= 0 && config.JitterFactor <= 1 {
			cfg.JitterFactor = config.JitterFactor
		}
	}

	rand.Seed(time.Now().UnixNano())
	var lastErr error

	for attempt := 0; attempt < cfg.MaxAttempts; attempt++ {
		// 执行函数
		if err := fn(); err == nil {
			return nil // 成功
		} else {
			lastErr = err
		}

		// 最后一次不等待
		if attempt == cfg.MaxAttempts-1 {
			break
		}

		// 计算等待时间
		delay := calculateDelay(attempt, cfg)
		log.Printf("Attempt %d failed, retrying in %v", attempt+1, delay)

		// 等待或取消
		select {
		case <-time.After(delay):
		case <-ctx.Done():
			return fmt.Errorf("context cancelled: %v", ctx.Err())
		}
	}

	return fmt.Errorf("after %d attempts, last error: %v", cfg.MaxAttempts, lastErr)
}

// calculateDelay 计算退避时间 (2^n + 随机抖动)
func calculateDelay(attempt int, cfg RetryConfig) time.Duration {
	// 指数增长：2^attempt * initialDelay
	exp := math.Pow(2, float64(attempt))
	delay := float64(cfg.InitialDelay) * exp

	// 应用上限
	if max := float64(cfg.MaxDelay); delay > max {
		delay = max
	}

	// 添加随机抖动
	jitter := delay * cfg.JitterFactor * rand.Float64()
	return time.Duration(delay + jitter)
}
